package com.qf.ability.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qf.ability.search.entity.GoodsInfoSearch;
import com.qf.ability.search.entity.HotWord;
import com.qf.ability.search.entity.SearchParams;
import com.qf.ability.search.service.IGoodsSearch;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.support.ValueType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * description:
 * author: Ken
 * 公众号：Java架构栈
 */
@Service
@Slf4j
public class GoodsSearchImpl implements IGoodsSearch {

    @Autowired
    private ElasticsearchRestTemplate restTemplate;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Override
    public boolean createIndex() {
        if(!isExsite()) {
            log.info("[index create] - 索引不存在，创建索引！");
            IndexOperations operations = restTemplate.indexOps(GoodsInfoSearch.class);
            if(operations.create()){
                //创建映射关系
                log.info("[index create] - 索引创建成功，创建映射关系！");
                Document mapping = operations.createMapping(GoodsInfoSearch.class);
                operations.putMapping(mapping);
            }
        }
        log.info("[index create] - 索引已经存在！");


        IndexOperations operations = restTemplate.indexOps(HotWord.class);
        if (!operations.exists()) {
            log.info("[index create] - 热搜词索引库不存在，创建索引库");
            operations.create();
            Document document = operations.createMapping(HotWord.class);
            operations.putMapping(document);
        }

        return true;
    }

    @Override
    public boolean isExsite() {
        IndexOperations operations = restTemplate.indexOps(GoodsInfoSearch.class);
        return operations.exists();
    }

    /**
     * 新增商品到ES
     * @param goodsInfoSearch
     * @return
     */
    @Override
    public boolean insert(GoodsInfoSearch goodsInfoSearch) {
        restTemplate.save(goodsInfoSearch);
        return true;
    }

    /**
     * 根据相关参数进行商品搜索
     * @param searchParams
     * @return
     */
    @Override
    public Map<String, Object> search(SearchParams searchParams) {
        log.info("[search] - 搜索服务查询的条件 - {}", searchParams);

        //搜索相似的关键词
        String likeKeyWord = this.searchLikeWord(searchParams.getKeyword());
        //将当前搜索关键词放入kafka中
        kafkaTemplate.send("hot_word_in", likeKeyWord.getBytes());


        //作为条件的规格值的列表
        Collection<Object> values = null;

        //判断是否存在规格
        if (!StringUtils.isEmpty(searchParams.getGuiges())) {
            JSONObject jsonObject = JSON.parseObject(searchParams.getGuiges());
            values = jsonObject.values();
        }
        
        //Nested 嵌套查询
        NestedQueryBuilder guigesQuery = QueryBuilders.nestedQuery("guiges",
                QueryBuilders.matchQuery("guiges.guigeVal.analyer", searchParams.getKeyword()),
                ScoreMode.Avg);

        //多字段查询
        MultiMatchQueryBuilder multiQuery = QueryBuilders.multiMatchQuery(searchParams.getKeyword())
                .field("title", 1.0f);
//                .field("title.pinyin", 0.5f);

        //主查询
        BoolQueryBuilder mainQuery = QueryBuilders.boolQuery()
                .should(multiQuery)
                .should(guigesQuery)
                .minimumShouldMatch(1);
        
        //处理规格的条件
        if (!Collections.isEmpty(values)) {
            for (Object value : values) {
                //获得一个规格的 嵌套查询 - 作为主查询的must部分
                NestedQueryBuilder nestGuigeQuery = QueryBuilders.nestedQuery("guiges", QueryBuilders.termQuery("guiges.guigeVal", value + ""), ScoreMode.Avg);
                mainQuery.must(nestGuigeQuery);
            }
        }

        NativeSearchQuery query = new NativeSearchQuery(mainQuery);

        //设置商品的聚合查询
        TermsAggregationBuilder aggregationBuilder = new TermsAggregationBuilder("myaggs", ValueType.NUMBER);
        aggregationBuilder.field("fid");//设置分组的字段
        aggregationBuilder.size(1);//只要显示第一个分组
        aggregationBuilder.order(BucketOrder.aggregation("_count", false));
        query.addAggregation(aggregationBuilder);

        //创建返回的Map集合
        Map map = new HashMap();

        //进行搜索
        SearchHits<GoodsInfoSearch> searchHits = restTemplate.search(query, GoodsInfoSearch.class);
        //获取搜索的结果
        List<SearchHit<GoodsInfoSearch>> results = searchHits.getSearchHits();
        //将搜索结果转换成商品集合
        List<GoodsInfoSearch> goodsList = results.stream()
                .map(goodsInfoSearchSearchHit -> goodsInfoSearchSearchHit.getContent())
                .collect(Collectors.toList());
        map.put("goodsList", goodsList);


        //判断结果是否存在聚合
        if (searchHits.hasAggregations()) {
            Aggregations aggregations = searchHits.getAggregations();
            //通过名称获取聚合的结果
            ParsedLongTerms myaggs = aggregations.get("myaggs");
            if (!CollectionUtils.isEmpty(myaggs.getBuckets())) {
                //获取聚合的结果
                Terms.Bucket bucket = myaggs.getBuckets().get(0);
                //获取桶的结果
                Long key = (Long) bucket.getKey();
                map.put("fid", key);
            }
        }

        return map;
    }

    /**
     * 查询相似的关键字
     * @param keyword
     * @return
     */
    @Override
    public String searchLikeWord(String keyword) {
        log.info("匹配相似的关键词 - {}", keyword);
        //相似性内容搜索
        MoreLikeThisQueryBuilder moreLikeThisQueryBuilder
                = QueryBuilders.moreLikeThisQuery(new String[]{"keyword"}, new String[]{keyword}, null);
        moreLikeThisQueryBuilder.minTermFreq(1);
        moreLikeThisQueryBuilder.minDocFreq(1);
        moreLikeThisQueryBuilder.minimumShouldMatch("70%");
        Query query = new NativeSearchQuery(moreLikeThisQueryBuilder);
        //执行搜索
        SearchHits<HotWord> result = restTemplate.search(query, HotWord.class);
        //获取搜索结果
        long count = result.getTotalHits();
        if (count > 0) {
            SearchHit<HotWord> hotWordSearchHit = result.getSearchHits().get(0);
            HotWord content = hotWordSearchHit.getContent();
            log.info("匹配结果 - {}", content.getKeyword());
            //返回相似词
            return content.getKeyword();
        }

        //不存在相似的词，则将当前的搜索词放入ES中
        HotWord hotWord = new HotWord();
        hotWord.setKeyword(keyword);
        restTemplate.save(hotWord);
        log.info("未匹配到相似结果 - {}", keyword);
        return keyword;
    }
}
